<?php

namespace App\Trip\Orders\BookOrders\SnapUp;

use App\Models\Setting\Setting;
use App\Models\Train\TrainSeat\Repositories\Interfaces\TrainSeatRepositoryInterface;
use App\Models\Train\TrainTripInfo\Repositories\Interfaces\TrainTripInfoRepositoryInterface;
use App\Trip\Train\Seat\SeatName;
use App\Trip\Train\Seat\SeatType;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\Str;
use JetBrains\PhpStorm\ArrayShape;


class SnapUpData
{
    private array   $snap_order_data;
    private Setting $setting;

    public function __construct(array $snap_order_data = [])
    {
        $this->snap_order_data = $snap_order_data;
        $this->setting         = new Setting();
    }

    public function getChooseSnapLevels(): array
    {
        return [
            [
                "level"   => 4,
                "name"    => "高速",
                "name_uy" => trans2uy("高速"),
                "price"   => $this->TrainSnapHighSpeedServiceFee(),
                "tip"     => "",
            ],
            [
                "level"   => 1,
                "name"    => "低速",
                "name_uy" => trans2uy("低速"),
                "price"   => $this->TrainSnapLowSpeedServiceFee(),
                "tip"     => "",
            ],
        ];
    }

    public function TrainSnapHighSpeedServiceFee(): float
    {
        return $this->setting->TrainSnapHighSpeedServiceFee();
    }

    public function TrainSnapLowSpeedServiceFee(): float
    {
        return 0;
    }

    public function calcSnapSuccessRate(): array
    {
        return [
            'rate'    => 0.1,
            'desc'    => '低',
            'desc_uy' => trans2uy('低'),
            'level'   => 1,
        ];
    }

    /**
     * 获取可抢票日期
     *
     * @param string $train_date
     *
     * @return array
     * @throws Exception
     */
    #[ArrayShape([
        'dates'    => 'array',
        'max_date' => 'string',
    ])]
    public function getDates(string $train_date = ''): array
    {
        if (isset($this->snap_order_data['train_date']) && $this->snap_order_data['train_date']) {
            $train_date = $this->snap_order_data['train_date'];
        } elseif (isset($this->snap_order_data['start_at']) && $this->snap_order_data['start_at']) {
            $train_date = $this->getTrainDate();
        }


        $book_days = $this->setting->getTrainBookDay();
        if (!Carbon::hasFormat($train_date, 'Y-m-d')) {
            throw new Exception('Invalid train date');
        }
        $train_date = Carbon::parse($train_date);
        if (Carbon::today()->greaterThan($train_date)) {
            throw new Exception('Invalid train date');
        }
        $max_date = Carbon::today()->addDays($book_days);
        if ($max_date->lessThan($train_date)) {
            throw new Exception('Invalid train date');
        }
        $ranges     = range(-2, 4);
        $weeks_desc = [
            0 => "周日",
            1 => "周一",
            2 => "周二",
            3 => "周三",
            4 => "周四",
            5 => "周五",
            6 => "周六",
            7 => "周日",
        ];

        $dates = collect($ranges)->map(function ($range) use ($max_date, $train_date, $weeks_desc) {
            $date = Carbon::parse($train_date)->addDays($range);
            if ($date->lessThan(Carbon::today()) || $date->greaterThan($max_date)) {
                return [];
            }
            $week = $date->weekday() ?: 7;
            $desc = $weeks_desc[$week];

            if ($date->isToday()) {
                $desc = "今天";
            } elseif ($date->isTomorrow()) {
                $desc = "明天";
            } elseif (Carbon::parse($date)->subDay()->isTomorrow()) {
                $desc = "后天";
            }
            return [
                'date'    => $date->toDateString(),
                'week'    => $week,
                'desc'    => $desc,
                'current' => $range == 0 ? 1 : 0,
                'today'   => $date->isToday(),
            ];
        })->filter()->values()->toArray();

        return [
            'dates'    => $dates,
            'max_date' => $max_date->toDateString(),
        ];
    }

    private function getTrainDate(): string
    {
        $start = $this->snap_order_data['start_at'] ?? '';
        return $start ? Carbon::parse($start)->toDateString() : '';
    }

    /**
     * 通过已指定坐席 获取选座信息
     *
     * @param array $data
     *
     * @return array
     */
    public function getSelectSeatsByAlternativeSeats(array $data = []): array
    {

        if (empty($data) && empty($this->snap_order_data)) {
            return [];
        }
        if (empty($data)) {
            $data = $this->snap_order_data;
        }

        $alternative_seats = $data['alternative_seats'] ?? [];
        $select_seats      = [];
        $hasSeat           = false;
        $hasBed            = false;

        foreach ($alternative_seats as $seat_name) {
            if (!$hasSeat) {
                $hasSeat = Str::contains($seat_name, "座") && $seat_name !== "无座";
            }

            if (!$hasBed) {
                $hasBed = Str::contains($seat_name, "卧");
            }
        }

        /**
         * 如果在指定坐席前
         */
        if (empty($alternative_seats)) {
            $seat_name = $data['seat_type'];
            if (!$hasSeat) {
                $hasSeat = Str::contains($seat_name, "座") && $seat_name !== "无座";
            }

            if (!$hasBed) {
                $hasBed = Str::contains($seat_name, "卧");
            }
        }

        if ($hasSeat) {
            $seat_type    = 1;
            $select_seats = [
                [
                    "seat"        => "窗",
                    "snap_desc"   => __("抢靠窗"),
                    "seat_type"   => $seat_type,
                    "service_fee" => SeatType::selectFee($seat_type),
                ],
                [
                    "seat"        => "过道",
                    "snap_desc"   => __("抢过道"),
                    "seat_type"   => $seat_type,
                    "service_fee" => SeatType::selectFee($seat_type),
                ],
            ];
        } elseif ($hasBed) {
            $seat_type    = 2;
            $select_seats = [
                [
                    "seat"        => "下铺,中铺",
                    "snap_desc"   => __("抢中下铺"),
                    "seat_type"   => $seat_type,
                    "service_fee" => SeatType::selectFee($seat_type),
                ],
            ];
        }

        return $select_seats;
    }

    public function getStopTimes(): array
    {
        return [
            [
                'time' => 35,
                'type' => 'minutes',
            ],
            [
                'time' => 60,
                'type' => 'minutes',
            ],
            [
                'time' => 120,
                'type' => 'minutes',
            ],
        ];
    }

    public function getMaxPriceBYAlternativeTrainsAndSeats(): array
    {
        return $this->getSnapAllSeats()['max_seat'];
    }

    /**
     * @param array $data
     *
     * @return array
     */

    #[ArrayShape([
        'seats'    => 'array',
        'max_seat' => 'array',
    ])]
    public function getSnapAllSeats(array $data = []): array
    {
        if (empty($data) && !empty($this->snap_order_data)) {
            $data = $this->snap_order_data;
        }

        $trainSeatRepo = app(TrainSeatRepositoryInterface::class);
        $trainTripRepo = app(TrainTripInfoRepositoryInterface::class);
        $getSeatsKey   = function ($data) {
            return $data['train_code']."|".$data['from_code']."|".$data['to_code'];
        };

        $getSeats           = function ($data) use ($trainSeatRepo) {
            return collect($trainSeatRepo->listByTrainCode($data['train_code'], $data['from_code'], $data['to_code']))->toArray();
        };
        $seats_key          = $getSeatsKey($data);
        $trains             = [];
        $seats[$seats_key]  = $getSeats($data);
        $alternative_trains = $data['alternative_trains'] ?? [];
        foreach ($alternative_trains as $alternative_train) {
            $seats_key          = $getSeatsKey($alternative_train);
            $seats[$seats_key]  = $getSeats($alternative_train);
            $trains[$seats_key] = $alternative_train;
        }
        /**
         * 从选中的座位里批数据
         */
        $alternative_seats = $data['alternative_seats'] ?? [];
        $can_choose_seats  = [];
        $default_seat      = [];
        $seat_trains       = [];
        $flatten_seats     = collect($seats)->flatten(1)->toArray();
        $max_seat          = [];
        $max_price         = 0;
        foreach ($flatten_seats as $seat) {
            $key          = $getSeatsKey($seat);
            $seat_name    = $seat['seat_name'];
            $seat_name_uy = SeatName::trans($seat_name, 'uy');
            $seat_price   = $seat['seat_price'];
            $trip_info    = $trainTripRepo->findOneByTrainAndStation($seat['train_code'], $seat['from_code'], $seat['to_code']);
            $train_date   = $arrive_date = $data['train_date'] ?? $this->getTrainDate();
            $day_diff     = $trains[$key]['arrive_time'] ?? ($trip_info?->getDayDifference() ?: '');
            $arrive_time  = $trains[$key]['arrive_time'] ?? ($trip_info?->getArriveTime() ?: '');
            $start_time   = $trains[$key]['start_time'] ?? ($trip_info?->getDepartTime() ?: '');
            if ($day_diff != '' && $day_diff > 0) {
                $arrive_date = Carbon::parse($arrive_date)->addDays($day_diff)->toDateString();
            }
            $start_at                  = $train_date." ".$start_time;
            $arrive_at                 = $arrive_date." ".$arrive_time;
            $seat_info                 = [
                'train_code'   => $seat['train_code'],
                'from_code'    => $seat['from_code'],
                'to_code'      => $seat['to_code'],
                'seat_price'   => $seat['seat_price'],
                'seat_name'    => $seat_name,
                'seat_name_uy' => $seat_name_uy,
                'arrive_time'  => $arrive_time,
                'start_time'   => $start_time,
                'arrive_at'    => $arrive_at,
                'start_at'     => $start_at,
                //				'trip_info'  => $trip_info,
            ];
            $seat_trains[$seat_name][] = $seat_info;

            if (!isset($can_choose_seats[$seat_name])) {
                $default = 0;
                if ($seat_name == $data['seat_type']) {
                    $default      = 1;
                    $default_seat = $seat_info;
                }

                $can_choose_seats[$seat_name] = [
                    'seat_name'    => $seat_name,
                    'seat_name_uy' => $seat_name_uy,
                    'seat_price'   => $seat_price,
                    'default'      => $default,
                ];
            } else if ($seat_price > $can_choose_seats[$seat_name]['seat_price']) {
                $can_choose_seats[$seat_name]['seat_price'] = $seat_price;
            }
            if (in_array($seat_name, $alternative_seats) && $seat_price > $max_price) {
                $max_price = $seat_price;
                $max_seat  = $seat_info;
            }
            $can_choose_seats[$seat_name]['seat_trains'] = $seat_trains[$seat_name] ?? [];
        }

        if (empty($max_seat)) {
            $max_seat = $default_seat;
        }

        return [
            'seats'    => collect($can_choose_seats)->sortBy('seat_price')->values()->toArray(),
            'max_seat' => $max_seat,
        ];
    }
}